package com.awakensoftware.hunterserver.utilities.geometry;

import org.geotools.geometry.jts.JTSFactoryFinder;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import com.vividsolutions.jts.util.GeometricShapeFactory;

public class GeometryHelper {

	public static Point createPoint(double xLongitude, double yLatitude) {
		Coordinate coordinate = new Coordinate(xLongitude, yLatitude);
		GeometryFactory geoFactory = JTSFactoryFinder.getGeometryFactory(null);
		Point point = geoFactory.createPoint(coordinate);
		return point;
	}

	public static Geometry createCircle(double currentXLongitude, double currentYLatitude, double radiusValue,
			LengthUnit radiusUnit) {
		double xLongitudeRadiusInDegree = getXLongitudeDegreesFromLength(radiusValue, radiusUnit, currentYLatitude);
		double yLatitudeRadiusInDegree = getYLatitudeDegreesFromLength(radiusValue, radiusUnit, currentYLatitude);
		GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
		shapeFactory.setNumPoints(Constants.NUMBER_OF_POINTS_PER_CIRCLE);
		shapeFactory.setCentre(new Coordinate(currentXLongitude, currentYLatitude));
		shapeFactory.setWidth(xLongitudeRadiusInDegree * 2);
		shapeFactory.setHeight(yLatitudeRadiusInDegree * 2);
		return shapeFactory.createCircle();
	}

	public static double getLengthInMeter(double originalValue, LengthUnit originalUnit) {
		switch (originalUnit) {
		case KILOMETER:
			return originalValue / 1000;
		default:
		case METER:
			return originalValue;
		}
	}

	/**
	 * @param wktGeometry
	 *            : Well-know text geometry
	 */
	public static Geometry createGeometryFromWKT(String WKTOfGeometry) {
		WKTReader wKTReader = new WKTReader();
		Geometry geom = null;
		try {
			geom = wKTReader.read(WKTOfGeometry);
		} catch (ParseException e) {
			return null;
		}
		return geom;
	}

	private static double getYLatitudeDegreesFromLength(double lengthValue, LengthUnit lengthUnit,
			double currentYLatitude) {
		double lengthInMeter = getLengthInMeter(lengthValue, lengthUnit);
		Double metersPerLatitudeDegree = getMetersPerLatitudeDegreeAtLatitude(currentYLatitude);
		if (metersPerLatitudeDegree == null) {
			return -1;
		}
		return lengthInMeter / metersPerLatitudeDegree;
	}

	private static double getXLongitudeDegreesFromLength(double lengthValue, LengthUnit lengthUnit, double currentYLatitude) {
		double lengthInMeter = getLengthInMeter(lengthValue, lengthUnit);
		Double metersPerLatitudeDegree = getMetersPerLatitudeDegreeAtLatitude(currentYLatitude);
		if (metersPerLatitudeDegree == null) {
			return -1;
		}
		Double metersPerLongitudeDegree = Math.cos(getRadiansFromDegree(currentYLatitude)) * metersPerLatitudeDegree;
		return lengthInMeter / metersPerLongitudeDegree;
	}

	private static double getMetersPerLatitudeDegreeAtLatitude(double currentYLatitude) {
		int currentClosestYLatitudeThreshold = (int) (Math.round(currentYLatitude / 10) * 10);
		Double metersPerLatitudeDegree = Constants.METERS_PER_LATITUDE_DEGREE_AT_LATITUDE
				.get(currentClosestYLatitudeThreshold);
		if (metersPerLatitudeDegree == null) {
			return -1;
		}
		return metersPerLatitudeDegree;
	}

	private static double getRadiansFromDegree(double degree) {
		return degree * Math.PI / 180;
	}

}
